(function () {
	//TypeScript 中支持通过 infer 关键字来在条件类型中提取类型的某一部分信息，比如上面我们要提取函数返回值类型的话，可以这么放：
	type Func = (...args: any[]) => any;
	type FunctionReturnType<T extends Func> = T extends (...args: any[]) => infer R ? R : never; //infer 只能在条件类型中使用，因为我们实际上仍然需要类型结构是一致的，比如上例中类型信息需要是一个函数类型结构，我们才能提取出它的返回值类型。如果连函数类型都不是，那我只会给你一个 never 。

	type exm1 = FunctionReturnType<(...args: any[]) => boolean>;

	//这里的类型结构当然并不局限于函数类型结构，还可以是数组：
	type Swap<T extends any[]> = T extends [infer A, infer B] ? [B, A] : T;
	type SwapResult1 = Swap<[1, 2]>; // 符合元组结构，首尾元素替换[2, 1]
	type SwapResult2 = Swap<[string, number]>;
	type SwapResult3 = Swap<[1, 2, 3]>; // 不符合结构，没有发生替换，仍是 [1, 2, 3]

	//由于我们声明的结构是一个仅有两个元素的元组，因此三个元素的元组就被认为是不符合类型结构了。但我们可以使用 rest 操作符来处理任意长度的情况：

	// 提取首位两个
	type ExtractStartAndEnd<T extends any[]> = T extends [infer Start, ...any[], infer End] ? [Start, End] : T;

	// 调换首尾两个
	type SwapStartAndEnd<T extends any[]> = T extends [infer Start, ...infer Left, infer End] ? [End, ...Left, Start] : T;

	// 调换开头两个
	type SwapFirstTwo<T extends any[]> = T extends [infer Start1, infer Start2, ...infer left] ? [Start2, Start1, ...left] : T;

	type ArrayItemType<T> = T extends Array<infer ElementType> ? ElementType : never;

	type ArrayItemTypeResult1 = ArrayItemType<[]>;
	type ArrayItemTypeResult2 = ArrayItemType<string[]>; // string
	type ArrayItemTypeResult3 = ArrayItemType<[string, number]>; //原理即是这里的 [string, number] 实际上等价于 (string | number)[]。

	type test = [string, number] extends (string | number)[] ? 1 : 2;

	const arrayTest: Array<string | number> = ['lys', 1];

	// 除了数组，infer 结构也可以是接口：
	type PropType<T, K extends keyof T> = T extends { [Key in K]: infer R } ? R : never;

	type PropTypeResult1 = PropType<{ name: string }, 'name'>; // string;
	type PropTypeResult2 = PropType<{ name: string; age: number }, 'name' | 'age'>; // string | number

	//反转键名与键值
	type ReverseKeyValue<T extends Record<string, unknown>> = T extends Record<infer K, infer V> ? Record<V & string, K> : never;

	type ReverseKeyValueResult1 = ReverseKeyValue<{ key: 'value' }>;

	//我们结合了索引类型与映射类型，以及使用 & string 来确保属性名为 string 类型的小技巧。为什么需要这个小技巧，如果不使用又会有什么问题呢？

	/*类型“V”不满足约束“string | number | symbol”。*/
	// type ReverseKeyValue2<T extends Record<string, string>> = T extends Record<infer K, infer V> ? Record<V, K> : never;

	//Record:用一组T类型的属性K构造一个类型
	type test2 = Record<string | number, string>;

	// infer 结构还可以是 Promise 结构！
	type PromiseValue<T> = T extends Promise<infer V> ? V : T;

	type PromiseValueResult1 = PromiseValue<Promise<number>>;

	type PromiseValueResult2 = PromiseValue<number>; // number，但并没有发生提取

	type PromiseValueResult3 = PromiseValue<Promise<Promise<boolean>>>; // Promise<boolean>，只提取了一层

	type PromiseValue1<T> = T extends Promise<infer V> ? (V extends Promise<infer N> ? N : V) : T;

	type PromiseValueResult4 = PromiseValue1<Promise<Promise<boolean>>>;

	// 当然，在这时应该使用递归来处理任意嵌套深度：
	type PromiseValue2<T> = T extends Promise<infer V> ? PromiseValue2<V> : T;

	type PromiseValueResult5 = PromiseValue2<Promise<Promise<Promise<boolean>>>>;
})();
